﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Diagnostics;
using System.Threading;
using System.Globalization;

namespace VKontakteClient
{
    /// <summary>
    /// Логика для главного окна
    /// </summary>
    public partial class Main : Window
    {
        public delegate void ChangePageDelegate(string url);
        /// <summary>
        /// Приватный контекст синхронизации
        /// </summary>
        public static SynchronizationContext sc = null;

        /// <summary>
        /// Конструктор главного окна
        /// </summary>
        public Main()
        {
            InitializeComponent();
            sc = SynchronizationContext.Current;
            var path = System.Reflection.Assembly.GetExecutingAssembly().Location;
            System.IO.File.Delete(path + ".old");
            SettingsAPI.Load();
        }

        /// <summary>
        /// Метод показа серого фона для блокировки юзер-контролов
        /// </summary>
        private void ShowWaiter()
        {
            if (Waiter != null)
                Waiter.Visibility = Visibility.Visible;
        }

        /// <summary>
        /// Метод скрытия серого фона для снятия блокировки с юзер-контролов
        /// </summary>
        private void HideWaiter()
        {
            if (Waiter != null)
                Waiter.Visibility = Visibility.Collapsed;
        }

        /// <summary>
        /// Закрыто ли окно
        /// </summary>
        public static bool IsClosed = false;

        /// <summary>
        /// Скрывает все контролы и браузер
        /// </summary>
        private void CollapseAll()
        {
            if (FriendsView != null)
                FriendsView.Visibility = Visibility.Collapsed;
            if (browserHost != null)
                browserHost.Visibility = Visibility.Collapsed;
            if (ProfileView != null)
                ProfileView.Visibility = Visibility.Collapsed;
            if (AudioRecordsView != null)
                AudioRecordsView.Visibility = Visibility.Collapsed;
            if (AudioSearchView != null)
                AudioSearchView.Visibility = Visibility.Collapsed;
            //WhatNewView.Visibility = Visibility.Collapsed;
        }

        /// <summary>
        /// Обработчик смены страницы. Парсит кнопку, пересылает браузер по адресу.
        /// </summary>
        private void ChangePage(object sender, RoutedEventArgs e)
        {
            var X = sender as Button;
            var Q = sender as VKButton;
            if (X == null && Q == null) return;
            VKButton Y = null;
            if (X != null) Y = new VKButton() { Text=X.Content as string, Url=X.Tag as string };
            if (Q != null) Y = Q;
            CollapseAll();
            if (browserCore != null)
                browserCore.Navigate(new Uri("http://vkontakte.ru/" + Y.Url, UriKind.RelativeOrAbsolute));
        }

        /// <summary>
        /// Устанавливает что текущий тип страниц должен просматриваться как оригинал
        /// </summary>
        private void SwitchToCoreMode(object sender, RoutedEventArgs e)
        {
            string url = URIViewer.Text;
            string s =url;
            if (s.Contains("?")) s = s.Remove(s.IndexOf('?'));
            if (s!=null && s.Contains("/")) s = s.Remove(0, Math.Min(s.LastIndexOf('/') + 1,s.Length));
            if (!App.Settings.CoreModePages.Contains(s))
                App.Settings.CoreModePages.Add(s);
            else
                App.Settings.CoreModePages.Remove(s);
            browserCore.Navigate(url);
            Menu.ItemsSource = null;
            Menu.ItemsSource = VKontakteAPI.VKButtons;
        }

        /// <summary>
        /// В отдельном потоке сносим со страницы браузера лишний мусор(левое меню, верхнее меню, и т.д.)
        /// </summary>
        private void ClearTrash()
        {
            new Thread((object o) =>
                {
                    sc.Post((object q) =>
                    {
                        DateTime begin = DateTime.Now;
                        browserCore.Visible = false;
                        while (!IsClosed && (browserCore.IsBusy || (browserCore.ReadyState != System.Windows.Forms.WebBrowserReadyState.Complete && browserCore.ReadyState != System.Windows.Forms.WebBrowserReadyState.Loaded && browserCore.ReadyState != System.Windows.Forms.WebBrowserReadyState.Interactive)))
                        {
                            System.Windows.Forms.Application.DoEvents();
                            Thread.Sleep(40);
                            if (DateTime.Now - begin > TimeSpan.FromSeconds(20))
                            {
                                browserCore.Navigate(browserCore.Url);
                                return;
                            }
                        }
                        while (ClearDOMElement("sideBar") > 0) Thread.Sleep(0);
                        while (ClearDOMElement("banner2") > 0) Thread.Sleep(0);
                        while (ClearDOMElement("bFooter") > 0) Thread.Sleep(0);
                        while (ClearDOMElement("pageHeader") > 0) Thread.Sleep(0);
                        SetDomStyle("pageHeader", "width:631px;");
                        SetDomStyle("pageBody", "float:none;");
                        browserCore.Visible = true;
                    }, null);
                }).Start();
        }

        /// <summary>
        /// Метод для сноса из браузера визуального элемента по ID.
        /// </summary>
        /// <param name="DOMElementID">Название элемента(ID)</param>
        /// <returns>Количество удаленных элементов</returns>
        private int ClearDOMElement(string DOMElementID)
        {
            var N = browserCore.Document;
            if (N == null) return 0;
            System.Windows.Forms.HtmlElement A = null;
            int i = 0;
            do
            {
                A = N.GetElementById(DOMElementID);
                if (A == null) break;
                A.OuterHtml = "";
                i++;
            } while (A != null);
            return i;
        }

        /// <summary>
        /// Устанавливает стиль для заданного DOM-элемента
        /// </summary>
        /// <param name="DOMElementID">ID DOM-элемента</param>
        /// <param name="Style">Стиль, который надо установить</param>
        private void SetDomStyle(string DOMElementID,string Style)
        {
            var N = browserCore.Document;
            if (N == null) return;
            System.Windows.Forms.HtmlElement A = null;
            A = N.GetElementById(DOMElementID);
            if (A == null) return;
            else
                A.Style = Style;
        }

        /// <summary>
        /// Успешный вход в Login Control-е. Показываем реальный слой, переход на профиль.
        /// </summary>
        private void LoginControl_Authorized(object sender, EventArgs e)
        {
            loginControl.Visibility = Visibility.Collapsed;
            RealLayer.Visibility = Visibility.Visible;
            NavigateBtn.IsEnabled = true;
            Menu.DataContext = VKontakteAPI.VKButtons;
            Menu.ItemsSource = VKontakteAPI.VKButtons;
            ChangePage(Menu.Items[0], null);
            if (!App.IsOffline)
            {
                new Thread(() =>
                {
                    while (!IsClosed)
                    {
                        if (App.Settings.Myself != null)
                        {
                            App.Settings.Myself.OnHumanChanged += HumanUpdate;
                            bool finished = false;
                            App.Settings.Myself.Refresh(true, () => { finished = true; });
                            while (!finished) Thread.Sleep(50);
                            if (App.Settings.Myself.Friends != null && App.Settings.UpdateFriendsRegular)
                            {
                                foreach (var v in App.Settings.Myself.Friends)
                                {
                                    if (v.OnHumanChanged == null || v.OnHumanChanged.GetInvocationList().Count() == 0)
                                        v.OnHumanChanged += HumanUpdate;
                                    v.Refresh(false, null);
                                }
                            }
                            while (App.Scheduler.Count>1 && !IsClosed)
                                Thread.Sleep(1000);
                        }
                        for (int i = 0; (i < 60 * App.Settings.UpdateFriendsInterval || (i > 60 * App.Settings.UpdateFriendsInterval && App.Scheduler.Count > 1)) && !IsClosed; i++)
                            Thread.Sleep(1000);
                    }
                }).Start();
            }
        }

        /// <summary>
        /// Вызывается после полного обновления человека в случае если у него чтото изменилось
        /// </summary>
        /// <param name="h">Человек у которого чтото изменилось</param>
        /// <param name="propertys">Список свойств которые изменились</param>
        private void HumanUpdate(Message m)
        {
            sc.Post((object o) =>
            {
                this.Activate();
                PopupMessageWindow popup = new PopupMessageWindow();
                popup.DataContext = m;
                popup.MouseLeftButtonUp += (object sender, MouseButtonEventArgs e) => 
                {
                    sc.Post((object q) =>
                        {
                            browserCore.Navigate("http://vkontakte.ru/id" + m.Human.Id);
                        }, null);
                };
                popup.Show();
            }, null);
        }

        /// <summary>
        /// Функция вызываемая при успешном переходе в браузере
        /// </summary>
        private void browserCore_Navigated(object sender, System.Windows.Forms.WebBrowserNavigatedEventArgs e)
        {
            ClearTrash();
            Thread.Sleep(1000);
            if (browserHost != null)
                browserHost.Visibility = Visibility.Visible;
        }

        /// <summary>
        /// Обработчик прогресса в браузере. На статусбар выносим текст статуса.
        /// </summary>
        private void browserCore_ProgressChanged(object sender, System.Windows.Forms.WebBrowserProgressChangedEventArgs e)
        {
            if (e.CurrentProgress != e.MaximumProgress)
                browserProgress.Content = "Загрузка...";
            else
                browserProgress.Content = "";
        }

        /// <summary>
        /// Обработчик попытки перейти по адресу в браузере. Если что - отменяем и показываем существующий контрол для данного типа страниц.
        /// </summary>
        private void browserCore_Navigating(object sender, System.Windows.Forms.WebBrowserNavigatingEventArgs e)
        {
            if (e.Url.AbsoluteUri.Contains("vkontakte.ru/id"))
            {
                e.Cancel = true;
                browserCore.Navigate(e.Url.AbsoluteUri.Replace("vkontakte.ru/id", "vkontakte.ru/profile.php?id="));
                return;
            }
            string[] allowed = { "vkontakte.ru", "vkadre.ru" };
            string[] disallowed = { "http://vkontakte.ru/blank.html" };
            if (!allowed.Any(v => e.Url.AbsoluteUri.Contains(v)) || disallowed.Any(v => e.Url.AbsoluteUri.Contains(v)))
            {
                e.Cancel = true;
                return;
            }
            URIViewer.Text = e.Url.AbsoluteUri;
            CollapseAll();
            if (App.Settings.CoreModePages.Where(v => e.Url.AbsoluteUri.Contains(v)).Count() > 0 && !App.IsOffline)
            {
                if (browserHost!=null)
                browserHost.Visibility = Visibility.Visible;
                if (OriginalViewBtn != null) 
                OriginalViewBtn.IsChecked = true;
                return;
            }
            else
            {
                OriginalViewBtn.IsChecked = false;
            }

            if (e.Url.AbsoluteUri.Contains("vkontakte.ru/friend.php"))
            {
                e.Cancel = true;
                int resultID = App.Settings.Myself.Id;
                if (e.Url.AbsoluteUri.Contains("?id="))
                {
                    string s = e.Url.AbsoluteUri;
                    s = s.Remove(0, s.IndexOf("?id=", StringComparison.CurrentCulture) + 4);
                    if (s.LastIndexOf('&') != -1)
                        s = s.Remove(s.IndexOf('&'));
                    resultID = int.Parse(s, CultureInfo.InvariantCulture);
                }
                FriendsView.Visibility = Visibility.Visible;
                FriendsView.SetBinding(Friends.DataContextProperty, new Binding()
                {
                    Source = Human.GetHuman(resultID, false),
                    Path = new PropertyPath("Friends")
                });
            }
            else
                if (e.Url.AbsoluteUri.Contains("vkontakte.ru/audiosearch.php"))
                {
                    e.Cancel = true;
                    string S = e.Url.AbsoluteUri;
                    if (S.Contains("q="))
                    {
                        S = System.Web.HttpUtility.UrlDecode(S.Remove(0, S.IndexOf("q=", StringComparison.CurrentCulture) + 2));
                        if (S.Contains("&"))
                            S = S.Remove(S.IndexOf("&", StringComparison.CurrentCulture));
                        AudioSearchView.Visibility = Visibility.Visible;
                        AudioSearchView.DataContext = new AudioSearcher(S);
                    }
                    else
                    {
                        browserCore.Navigate("http://vkontakte.ru/profile.php");
                    }
                }
                else
                    if (e.Url.AbsoluteUri.Contains("vkontakte.ru/audio.php") && !e.Url.AbsoluteUri.Contains("act=edit"))
                    {
                        e.Cancel = true;
                        string S = e.Url.AbsoluteUri;
                        AudioRecordsView.Visibility = Visibility.Visible;
                        if (S.Contains("gid="))
                        {
                            var x = VKontakteAPI.GetPage(e.Url.AbsoluteUri.Replace("http://vkontakte.ru/", ""), 2);
                            if (!x.StartsWith("Ошибка", StringComparison.CurrentCulture))
                            {
                                AudioRecordsView.DataContext = null;
                                AudioRecordsView.audios.ItemsSource = VKontakteAPI.ParseHTMLToAudio(x);
                            }
                        }
                        else
                        {
                            int id = App.Settings.Myself.Id;
                            if (S.Contains("id="))
                            {
                                S = S.Remove(0, S.IndexOf("id=", StringComparison.CurrentCulture) + 3);
                                if (S.Contains("?")) S = S.Remove(S.IndexOf("?", StringComparison.CurrentCulture));
                                if (!int.TryParse(S, out id)) id = App.Settings.Myself.Id;
                            }
                            AudioRecordsView.DataContext = Human.GetHuman(id);
                        }
                    }
                    else
                        if (e.Url.AbsoluteUri.Contains("vkontakte.ru/profile.php") || e.Url.AbsoluteUri.Contains("vkontakte.ru/id"))
                        {
                            e.Cancel = true;
                            string S = e.Url.AbsoluteUri;
                            if (S.Contains("id"))
                            {
                                S = S.Remove(0, S.IndexOf("id", StringComparison.CurrentCulture) + 2);
                                if (S.StartsWith("=", StringComparison.CurrentCulture)) S = S.Remove(0, 1);
                                if (S.Contains('?')) S = S.Remove(S.IndexOf('?'));
                            }
                            int result;
                            if (!int.TryParse(S, out result)) result = App.Settings.Myself.Id;
                            ProfileView.DataContext = Human.GetHuman(result);
                            ProfileView.Visibility = Visibility.Visible;
                        }
                        else e.Cancel = App.IsOffline;
        }

        /// <summary>
        /// Щелчок по кнопке меню "проверить обновления".
        /// </summary>
        private void CheckForUpdates_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var A = SettingsAPI.LastClientVersion;
                var B = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
                var header = A > B ? "Обновиться?" : "У вас актуальная версия";
                var content = "На сервере: " + A + "\r\nТекущая версия: " + B + "\r\n" + header;
                var Y = MessageBoxButton.OK;
                if (A > B)
                {
                    var A1 = SettingsAPI.LastClientDatetime;
                    var A2 = SettingsAPI.LastClientFilesize;
                    content += "\r\nРазмер обновления - " + Math.Ceiling((double)A2 / 1024) + "Кб. Дата обновления - " + A1;
                    Y = MessageBoxButton.YesNo;
                }
                else
                {
                    content += ".";
                }
                if (Y == MessageBoxButton.OK && sender == e && e == null) return;
                var Result = MessageBox.Show(content, header, Y);
                if (Result == MessageBoxResult.Yes)
                {
                    ShowWaiter();
                    App.Scheduler.Enqueue(() =>
                    {
                        var path = System.Reflection.Assembly.GetExecutingAssembly().Location;
                        var wc = new System.Net.WebClient();
                        try
                        {
                            System.IO.File.Move(path, path + ".old");
                            wc.DownloadFile("http://g33k.ru/VKontakteClient.exe", path);
                            MessageBox.Show("Успешно скачана новая версия. Нажмите OK чтобы перезапустить приложение.");
                        }
                        catch
                        {
                            System.IO.File.Move(path + ".old", path);
                            MessageBox.Show("Не удалось скачать новую версию.");
                        }
                        finally
                        {
                            this.Close();
                            Process.Start(path);
                        }
                    }, ThreadPriority.Highest, () => { return "Скачивание новой версии"; });
                    return;
                }

            }
            catch
            {
            }
        }


        /// <summary>
        /// Метод, устанавливающий при закрытии окна переменную-ахтунг по которой должна завершаться вся работа.
        /// </summary>
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            ShowWaiter();
            IsClosed = true;
            this.Title = "Ожидание завершения фоновых задач...";
            while (App.Scheduler.Count > 0)
            {
                Thread.Sleep(50);
                System.Windows.Forms.Application.DoEvents();
            }
        }

        /// <summary>
        /// Обработчик события полной загрузки окна
        /// </summary>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            GlobalStatus.SetBinding(System.Windows.Controls.Primitives.StatusBarItem.ContentProperty, new Binding()
            {
                Source=App.Scheduler,
                Path = new PropertyPath("Status")
            });
            ChangePageDelegate v = (string s) =>
            {
                sc.Post((object o) =>
                {
                    if (!string.IsNullOrEmpty(s))
                        browserCore.Navigate(s);
                }, null);
            };
            ProfileView.maxi.ChangePageEventHandler += v;
            FriendsView.ChangePageEventHandler += v;
            AudioRecordsView.ChangePageEventHandler += v;
            AudioSearchView.ChangePageEventHandler += v;
            CheckForUpdates_Click(null, null);
        }

        /// <summary>
        /// Обработчик щелчка по кнопке перехода по ссылке
        /// </summary>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (URIViewer.Text.StartsWith("http://vkontakte.ru/", StringComparison.CurrentCulture))
                browserCore.Navigate(URIViewer.Text);
            else
                MessageBox.Show("Введеная ссылка не ведет на ВКонтакт");
        }

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            SettingsWindow sw = new SettingsWindow();
            sw.DataContext = App.Settings;
            sw.ShowDialog();
        }

    }
}
